case study_face detection

Face Detection


from __future__ import print_function
from preprocess import FaceDetector
import argparse
import cv2 

ap = argparse.ArgumentParser()
ap.add_argument("-f", "--face", required = True,
    help = "path to where the face cascade resides")
ap.add_argument("-i", "--image", required = True,
    help = "path to where the image file resides")
args = vars(ap.parse_args())

# load the image and convert it to grayscale
image = cv2.imread(args["image"])
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)

首先导入必要的包,为了保持代码的整洁,在同目录下新建了一个preprocess文件夹,然后新建一个init.py文件,使得python解释器将该文件夹解释为包。在该文件夹下还有个facedetector.py文件。不懂init.py的参考前面的文章。然后我们解析参数,读取图片并将图片转化为灰度图像。

但是,让我们不要走得太远。在我们考虑在图像中寻找面部之前,我们首先需要定义一个类来处理我们如何在图像中找到面部。

新建一个facedetector.py文件(该文件就是上面导入的包)

import cv2 

class FaceDetector:
    def __init__(self,faceCascadepath):
        # load the face detector 
        self.faceCascade = cv2.CascadeClassifier(faceCascadepath)

    def detect(self,image,scaleFactor = 1.1,minNeighbors=5,minSize=(30,30)):
        # detect faces in the image 
        rects = self.faceCascade.detectMultiScale(image,
            scaleFactor=scaleFactor,minNeighbors=minNeighbors,
            minSize=minSize,flags=cv2.CASCADE_SCALE_IMAGE)

        # return the rectangles representing bounding
        # boxes around the faces 
        return rects 

为了构建人脸识别软件,我们采用在OpenCV中内置的Haar级联分类器。这些分类器已经过预先训练以识别面孔!

构建我们自己的分类器当然不在本案例研究的范围之内。但如果我们想,我们需要很多“积极”和“消极”的图像。正图像将包含具有面部的图像,而负面图像将包含没有面部的图像。基于此数据集,我们可以提取特征来表征图像中的面部(或没有面部)并构建我们自己的分类器。这将是很多工作,而且非常耗时。

无论如何,这些分类器通过以不同的比例尺寸从左到右,从上到下扫描图像来工作。从左到右,从上到下扫描图像称为“滑动窗口(sliding window)”方法。

当窗口从左向右和从上到下移动时,一次一个像素,根据我们提供给分类器的参数,询问分类器是否“认为”在当前窗口中存在面部。

在类里面,我们定义了构造函数,该函数需要一个参数——我们级联分类器所在的路径。此分类器被序列化为XML文件。对cv2.CascadeClassifier的调用将对分类器进行反序列化,将其加载到内存中,并允许我们检测图像中的面部。

为了在图像中实际找到面部,我们接着定义了检测方法。该函数需要一个必需参数,即想要找到面部的图像,后跟三个可选参数。让我们来看看这些参数意味着什么:

scaleFactor:图像大小在每个图像尺度上减少了多少。这个值用于创建缩放金字塔,以便检测图像中多个缩放的面部(一些面可能更接近前景,因此更大;其他面可能更小并且在背景中,因此使用不同的缩放)。比如设置值为1.05,则表明我们在金字塔中的每个级别将图像的大小减小了5%。

minNeighbors:每个窗口应该有多少个neighbors才能将窗口中的区域视为一个脸。级联分类器将检测面部周围的多个窗口。此参数控制需要检测多少矩形(Neighbors)才能将窗口标记为面部。

minSize:宽度和高度(以像素为单位)的元组,表示窗口的最小尺寸。小于此大小的边界框将被忽略。从(30,30)开始并从那里进行微调是一个好主意。

通过调用在FaceDetector类的构造函数中创建的分类器的detectMultiScale方法,处理检测图像中的实际面部。我们使用我们默认的scaleFactorminNeighborsminSize,然后该方法为我们完成了整个人脸检测过程!

然后,detectMultiScale方法返回rects,这是一个包含图像中面部边界框的元组列表。这些边界框只是面部的(x,y)位置,以及框的宽度和高度。

接着,让我们继续detect_faces.py文件的编写,来实现我们自己的图像面部识别。

文件detect_faces.py

# find faces in the image
fd = FaceDetector(args["face"])
faceRects = fd.detect(gray,scaleFactor=1.1,minNeighbors=5,
    minSize=(30,30))
print("I found {} face(s)".format(len(faceRects)))    

# loop over the faces and draw a rectangle around each
for (x,y,w,h) in faceRects:
    cv2.rectangle(image,(x,y),(x + w,y + h),(0,255,0),2)

# show the detected faces
cv2.imshow("Faces",image)
cv2.waitKey(0)

我们首先实例化我们的FaceDetector类,提供XML分类器的路径作为唯一参数。通过调用detect方法检测传入图像中的实际面部。然后打印我们在图像中一共找到了几个faces。

但是为了在图像周围实际绘制一个边界框,我们需要单独循环它们。同样,每个边界框只是一个有四个值的元组:在图像中,x和y为起始位置,然后是脸部的宽度和高度。

cv2.rectangle的调用会在face上绘制一个绿色框。最后执行我们的脚本。

文件目录

执行脚本文件

F:\20181116\Case Studies, 3nd Edition\face detect>python detect_faces.py --image "My Snapshot.jpg" --face cascades\haarcascade_frontalface_default.xml
I found 1 face(s)

显示结果:

需要注意的是,图片最好不要有中文路径,不然报错

F:\20181116\Case Studies, 3nd Edition\face detect>python detect_faces.py -image GDP组合.jpg --face cascades\haarcascade_frontalface_default.xml l
usage: detect_faces.py [-h] -f FACE -i IMAGE
detect_faces.py: error: unrecognized arguments: GDP组合.jpg

也许这看上去好像很完美了,但是当我们多测试几张图片的时候,发现了如下的结果

F:\20181116\Case Studies, 3nd Edition\face detect>python detect_faces.py --i spurs.jpg --face cascades\haarcascade_frontalface_default.xml
I found 3 face(s)

明明没有Tim的脸,为啥检测到了三个脸呢?

答案在于我们上面讨论过的cv2.detectMutliScale函数的参数。这些参数往往是敏感的,一组图像的某些参数选择不适用于另一组图像。

在大多数情况下,罪魁祸首将是scaleFactor参数。在其他情况下,它可能是minNeighbors。但作为调试规则,从scaleFactor开始,根据需要进行调整,然后转到minNeighbors

考虑到这个调试规则,我们首先改变了对FaceDetector检测方法的调用:

faceRects = fd.detect(gray,scaleFactor=1.3,minNeighbors=5,
    minSize=(30,30))

唯一的变化是scaleFactor参数,将其从1.1更改为1.3。

我们在看一次结果:

很显然,这一次结果是正确的,只有两个面部(虽然石佛和妖刀退役了,跑车也远离了圣城),但无论如何我们的结果是正确的。

完整代码:

链接:https://pan.baidu.com/s/16rPsfhpV8pbSd-fKvAJ07w 密码:d4c9

更多的参考:

Case Studies – Face Detection

Rapid Object Detection using a Boosted Cascade of Simple Features

HOG + Linear SVM framework,

image pyramid

sliding windows

haarcascades

pre-trained HOG detector to detect pedestrians in images


---------------- The End ----------------
支持一下
Fork me on GitHub ;